﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace game
{
    public class Shield
    {
        protected static ModelUnit sphereModel;
        protected static Effect effect;
        protected static EffectTechnique effectTech;
        protected static EffectParameter ep_gWorld;
        protected static EffectParameter ep_gViewProj;
        protected static EffectParameter ep_gEyePosW;
        protected static EffectParameter ep_gColor;
        protected static EffectParameter ep_gShieldCenterW;
        protected static EffectParameter ep_gTex;
        protected static EffectParameter ep_gHitPos;
        protected static EffectParameter ep_gHitElapsedTime;

        protected static Texture texture;

        protected Scene scene;
        protected Ship ownerShip;
        protected float radius;
        protected Color color;
        protected float maxResistance;

        protected Matrix scale;
        protected Vector3 rotateVel;
        protected Vector3 rotation;

        public bool On { get; protected set; }

        public float LoadLevel { get { return Math.Min((float)curLoadTimeMs / (float)maxLoadTimeMs, 1.0f); } }
        public float LifetimeLevel { get { return 1.0f - Math.Min((float)curLifeTimeMs / (float)maxLifeTimeMs, 1.0f); } }

        protected float turnOnLevel = 0.0f;
        protected float fadeSpeed = 0.002f;
        protected float turnOnSpeed = 0.003f;

        protected int maxLoadTimeMs = 25000;
        protected int curLoadTimeMs = 25000;

        protected int maxLifeTimeMs = 5000;
        protected int curLifeTimeMs = 0;

        protected bool hit = false;
        protected float maxImpactTimeMs = 250;
        protected float currentImpactTimeMs = 0;

        protected Vector3 hitPosition;

        protected bool fading = false;
        protected bool turningOn = false;

        protected PointLight light;

        protected Vector3 prevPosition;

        static Shield()
        {
            sphereModel = new ModelUnit(ShooterGame.Instance.Content.Load<Model>("Models\\kula"));
            effect = ShooterGame.Instance.Content.Load<Effect>("Effects\\Shield");
            effectTech = effect.Techniques["Shield"];
            ep_gWorld = effect.Parameters["gWorld"];
            ep_gViewProj = effect.Parameters["gViewProj"];
            ep_gEyePosW = effect.Parameters["gEyePosW"];
            ep_gColor = effect.Parameters["gColor"];
            ep_gShieldCenterW = effect.Parameters["gShieldCenterW"];
            ep_gTex = effect.Parameters["gTex"];
            ep_gHitPos = effect.Parameters["gHitPos"];
            ep_gHitElapsedTime = effect.Parameters["gHitElapsedTime"];

            texture = ShooterGame.Instance.Content.Load<Texture>("Textures\\shield\\396-v3");
        }

        public Shield(Scene scene, Ship ownerShip, float radius, Color color, float maxResistance)
        {
            this.scene = scene;
            this.ownerShip = ownerShip;
            this.radius = radius;
            this.color = color;
            this.maxResistance = maxResistance;

            //TurnOn();

            scale = Matrix.CreateScale(radius);

            rotateVel = RandomHelper.RandomVector();
            rotateVel.Normalize();

            light = new PointLight(false, ownerShip.Position);
        }

        public bool TurnOn()
        {
            if (!On && curLoadTimeMs >= maxLoadTimeMs)
            {
                On = true;
                turningOn = true;
                fading = false;
                turnOnLevel = 0.0f;
                curLifeTimeMs = 0;
                color.A = 0;

                light.Position = ownerShip.Position;
                light.GrowSpeed = turnOnSpeed;
                light.CollapseSpeed = fadeSpeed;
                light.Diffuse = color.ToVector4();
                light.LifeTimeMs = maxLifeTimeMs;
                light.Attenuation012 = new Vector3(0, 0, 0.3f);
                light.Start();
                scene.Lights.Add(light);

                return true;
            }
            return false;
        }

        public void Hit(float damage, Vector3 hitPos)
        {
            Vector3 hitDir;
            Vector3 currPos = ownerShip.Position;
            Vector3.Subtract(ref hitPos, ref currPos, out hitDir);
            hitDir.Normalize();

            Vector3.Multiply(ref hitDir, 1625, out hitDir);
            Vector3.Add(ref currPos, ref hitDir, out hitPosition);

            hit = true;
            currentImpactTimeMs = 0;
            int lifeTimeLossMs = (int)((damage / maxResistance) * (float)maxLifeTimeMs);
            curLifeTimeMs += lifeTimeLossMs;
            light.ShortenLifeBy(lifeTimeLossMs);
        }

        public void Update(GameTime gameTime)
        {
            if (!On)
            {
                curLoadTimeMs += gameTime.ElapsedGameTime.Milliseconds;
                return;
            }

            if (fading)
            {
                if (turnOnLevel <= 0.0f)
                {
                    On = false;
                    curLoadTimeMs = 0;
                }
                else
                {
                    turnOnLevel -= fadeSpeed * gameTime.ElapsedGameTime.Milliseconds;
                    turnOnLevel = Math.Max(turnOnLevel, 0.0f);
                }
            }
            else if (turningOn)
            {
                if (turnOnLevel >= 1.0f)
                {
                    turningOn = false;
                    turnOnLevel = Math.Min(turnOnLevel, 1.0f);
                }
                else
                    turnOnLevel += turnOnSpeed * gameTime.ElapsedGameTime.Milliseconds;
            }
            else
            {
                if (curLifeTimeMs >= maxLifeTimeMs)
                    fading = true;
                else
                    curLifeTimeMs += gameTime.ElapsedGameTime.Milliseconds;
            }

            color.A = (byte)(turnOnLevel * 255.0f);
            //color.R = (byte)((1.0f - turnOnLevel) * 255.0f);

            rotation += rotateVel * 0.0010f * gameTime.ElapsedGameTime.Milliseconds;
            rotation.X = RandomHelper.RandomBetween(0, MathHelper.TwoPi);
            rotation.Y = RandomHelper.RandomBetween(0, MathHelper.TwoPi);
            rotation.Z = RandomHelper.RandomBetween(0, MathHelper.TwoPi);

            if (hit)
            {
                currentImpactTimeMs += gameTime.ElapsedGameTime.Milliseconds;
                if (currentImpactTimeMs > maxImpactTimeMs)
                    hit = false;
            }

            light.Position = ownerShip.Position + new Vector3(0,500,0);
            light.Diffuse = color.ToVector4();


            // updating hitPosition so it stays in "local" space
            Vector3 position = ownerShip.Position;
            Vector3 posDiff;
            Vector3.Subtract(ref position, ref prevPosition, out posDiff);
            Vector3.Add(ref hitPosition, ref posDiff, out hitPosition);
            prevPosition = position;
        }

        public void Draw(GameTime gameTime)
        {
            if (!On)
                return;

            sphereModel.SetEffect(effect);
            Matrix viewProj = scene.ActiveCamera.View * scene.ActiveCamera.Projection;

            ep_gViewProj.SetValue(viewProj);
            ep_gEyePosW.SetValue(scene.ActiveCamera.Position);
            ep_gShieldCenterW.SetValue(ownerShip.Position);
            //ep_gTex.SetValue(texture);
            ep_gHitPos.SetValue(hitPosition);

            if (!hit)
            {
                ep_gColor.SetValue(color.ToVector4());
                ep_gHitElapsedTime.SetValue(0.0f);
            }
            else
            {
                ep_gColor.SetValue(Color.Red.ToVector4());
                ep_gHitElapsedTime.SetValue(currentImpactTimeMs);
            }

            Matrix rot = Matrix.CreateRotationZ(rotation.Z) *
                         Matrix.CreateRotationZ(rotation.Y) *
                         Matrix.CreateRotationZ(rotation.X);

            Matrix translation = Matrix.CreateTranslation(ownerShip.Position);

            foreach (ModelMesh mesh in sphereModel.Model.Meshes)
            {
                ep_gWorld.SetValue(scale * rot * sphereModel.transforms[mesh.ParentBone.Index] * translation);
                mesh.Draw();
            }
            sphereModel.SetDefaultEffects();
        }
    }
}
